package cn.hftoday.db;

import java.util.*;
import org.apache.log4j.*;

/**
 * database connection pool
 * @author lane.cn@gmail.com
 *
 */
public class ConnectionPool {
	private static final Logger LOG = Logger.getLogger(ConnectionPool.class);
	private static ConnectionPool _instance = null;
	
	private List<String> _names = new ArrayList<String>();
	private List<String> _types = new ArrayList<String>();
	private List<String> _connectionStrings = new ArrayList<String>();
	
	private HashMap<String, DatabaseConnection> connections = new HashMap<String, DatabaseConnection>();
	
	private ConnectionPool() {
		//private constractor
	}
	
	/**
	 * static instance of connection pool
	 * @return
	 */
	public static ConnectionPool getInstance() {
		if (_instance == null) {
			_instance = new ConnectionPool();
		}
		return _instance;
	}
	
	/**
	 * add database connection
	 * @param name
	 * @param type
	 * @param connectionString
	 */
	public void addConnection(String name, String type, String connectionString) {
		_names.add(name);
		_types.add(type);
		_connectionStrings.add(connectionString);
		LOG.info("add database connection: " + name + ", " + type);
	}
	
	/**
	 * get database connection by name
	 * @param name
	 * @return
	 */
	public DatabaseConnection getConnection(String name) {
		String key = name + "#" + Thread.currentThread().getName() + "#" + Thread.currentThread().getId();
		//LOG.debug("get out : " + key);
		return connections.get(key);
	}

	/**
	 * open database connection for current thread
	 */
	public void open() {
		for (int i = 0; i < _names.size(); i ++) {
			if (!connections.containsKey(_names.get(i))) {
				DatabaseConnection conn = (DatabaseConnection)createInstance(_types.get(i));
				conn.setName(_names.get(i));
				conn.setConnectionString(_connectionStrings.get(i));
				conn.open();
				String key = _names.get(i) + "#" + Thread.currentThread().getName() + "#" + Thread.currentThread().getId();
				//LOG.debug("put in : " + key);
				connections.put(key, conn);
			}
		}
	}
	
	/**
	 * close database connection
	 */
	public void close() {
		for (int i = 0; i < _names.size(); i ++) {
			try {
				getConnection(_names.get(i)).close();
			} 
			catch (Exception e) {}
			try {
				connections.remove(_names.get(i) + "#" + Thread.currentThread().getName() + "#" + Thread.currentThread().getId());
			}
			catch (Exception e) {}
		}
	}

	/**
	 * begin transaction to all connections in pool
	 */
	public void beginTransaction() {
		for (int i = 0; i < _names.size(); i ++) {
			try {
				getConnection(_names.get(i)).beginTransaction();
			}
			catch (Exception e) {
				LOG.error("error.db.begintransaction", e);
				throw new RuntimeException("error.db.begintransaction", e);
			}
		}
	}
	
	/**
	 * commit transaction to all connections in pool
	 */
	public void commit() {
		for (int i = 0; i < _names.size(); i ++) {
			try {
				getConnection(_names.get(i)).commit();
			}
			catch (Exception e) {
				LOG.error("error.db.commit", e);
				throw new RuntimeException("error.db.commit", e);
			}
		}
	}

	/**
	 * rollback transaction to all connections in pool
	 */
	public void rollback() {
		for (int i = 0; i < _names.size(); i ++) {
			try {
				getConnection(_names.get(i)).rollback();
			}
			catch (Exception e) {
				LOG.error("error.db.rollback", e);
			}
		}
	}
	
	/**
	 * create instance by name
	 * @param type
	 * @return
	 */
	private Object createInstance(String type) {
		try {
			Class c = Class.forName(type);
			Object o = c.newInstance();
			return o;
		} 
		catch (Exception e) {
			return null;
		}
	}
}
